<?php
/**
 * Note: 你好现在支付 无需进行限额/切换管理
 */
namespace App\Services\Pay;

use App\Abstracts\PayAbstract;
use App\Caches\Game\AppSourceCache;
use App\Caches\Game\PayChannelCache;
use App\Exceptions\PayChannelEmptyException;
use App\Models\Game\Center\PayOrder;
use App\Models\Game\Center\WithdrawOrder;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;

class PayYajie extends PayAbstract
{
    public const CHANNEL = 'yajie';

    private const API_ALIPAY = '/api/zfb/makeOrder';
    private const API_QUERY_ORDER = '/api/queryOrder';
    private const API_QUERY_BALANCE = '/api/queryBalance';


    /**
     * 生成网关
     * @param $order
     * @return PayYajie
     * @throws PayChannelEmptyException
     */
    public function getPaymentByOrder($order): PayYajie
    {
        $appSource = AppSourceCache::get($order->source);
        if ($order instanceof PayOrder) {
            $this->payChannel = $this->getUseablePayChannel(self::CHANNEL, $order->price, $appSource->type);
        } else if ($order instanceof WithdrawOrder) {
            $this->payChannel = $this->getUseableWithdrawChannel(self::CHANNEL, $order->money, $appSource->type);
        } else {
            throw new \Exception('invalid param: order');
        }
        $this->getConfig($this->payChannel);
        return $this;
    }

    /**
     * 根据 channelId 获取 payment
     * @param int $payChannelId
     * @return PayYajie
     */
    public function getPaymentByPayChannelId(int $payChannelId): PayYajie
    {
        $this->payChannel = PayChannelCache::get($payChannelId);
        $this->getConfig($this->payChannel);
        return $this;
    }

    /**
     * 发送get请求
     * @param $url
     * @param $data
     * @return Response
     * @throws \Exception
     */
    protected function requestGet($url, $data): Response
    {
        // 新旧地址不兼容, 使用数据库配置 + 接口进行拼接
        $url = $this->payChannel->api_url . $url;

        // 发生错误时 100ms 后重试 1 次, 每次超时3s (设置timeout之后, 如果超时会直接抛出异常, 不到 $response->throw), 这里放入try
        try {
            info(__METHOD__, [$url, $data]);
            $response = Http::retry(2, 100)->timeout(3)->asForm()->get($url, $data);
            $response->throw();
            info(__METHOD__, [$response]);
        } catch (\Exception $e) {
            report($e);
            throw new \Exception("服务正在维护中，请联系客服");
        }
        return $response;
    }

    /**
     * 生成签名
     * @param array $params
     * @return string
     */
    protected function makeSign(array $params): string
    {
        ksort($params);
        if (isset($params['sign'])) unset($params['sign']);

        $signStr = '';
        foreach ($params as $v) {
            if (!empty($v)) $signStr .= $v;
        }
        $signStr .= $this->payChannel->appkey;
        return md5($signStr);
    }

    /**
     * 签名校验
     * @param array $params
     * @return bool
     */
    public function verifySign(array $params): bool
    {
        $sign = md5($params['extOrderId'].$params['orderId'].$params['state'].$this->payChannel->appkey);
        return $sign === $params['sign'];
    }

    /**
     * @param WithdrawOrder $withdrawOrder
     * @param string $alipayUserId
     * @param string $alipayAccount
     * @param string $alipayRealName
     * @param string $notifyUrl
     * @return bool
     * @throws \Exception
     */
    public function makeOrder(WithdrawOrder $withdrawOrder, string $alipayUserId, string $alipayAccount, string $alipayRealName, string $notifyUrl): bool
    {
        $params['merchantId'] = $this->payChannel->appid;
        $params['alipayUserId'] = $alipayUserId;
        $params['incomeAccount'] = $alipayAccount;
        $params['incomeUserName'] = $alipayRealName;
        $params['incomeRemark'] = '';
        $params['num'] = $withdrawOrder->money/100; // 元
        $params['extOrderId'] = $withdrawOrder->orderid;
        $params['notifyUrl'] = $notifyUrl;
        $params['sign'] = md5($params['extOrderId'].$params['alipayUserId'].$params['incomeAccount'].$params['incomeRemark'].$params['incomeUserName'].$params['merchantId'].$params['notifyUrl'].$params['num'].$this->payChannel->appkey);

        $response = $this->requestGet(self::API_ALIPAY, $params);
        if ($response['code'] != 0) {
            throw new \Exception("{$response['code']}: {$response['msg']}");
        }
        return true;
    }

    /**
     * 订单校验
     * @param WithdrawOrder $withdrawOrder
     * @return bool
     */
    public function withdrawOrderVerify(WithdrawOrder $withdrawOrder): bool
    {
        $params['merchantId'] = $this->payChannel->appid;
        $params['extOrderId'] = $withdrawOrder->orderid;
        $params['sign'] = $this->makeSign($params);
        try {
            $response = $this->requestGet(self::API_QUERY_ORDER, $params);
        } catch (\Exception $e) {
            report($e);
            return false;
        }

        // 4成功 5失败 3充值中 9未查到订单（如果下单成功但是未查到订单不可以置为失败，需要进行核对） 200003 ip不符 200004 账户已经被锁定
        return $response['code'] == 0 && $response['data'] == 4;
    }

    /**
     * 获取账户余额
     * @return int
     * @throws \Exception
     */
    public function getBalance(): int
    {
        $params['merchantId'] = $this->payChannel->appid;
        $params['sign'] = $this->makeSign($params);

        $response = $this->requestGet(self::API_QUERY_BALANCE, $params);
        if ($response['code'] != 0) {
            throw new \Exception("{$response['code']}: {$response['msg']}");
        }
        return $response['ext']['balance'] ?? 0;
    }
}
